#include "manualPWM.h"
#include "common.h"
#include "stm32f10x_conf.h"
#include "stm32f10x_gpio.h"
#include <stdlib.h>
#include <string.h>

// copied from TIMRegister.c
static inline uint16_t _TIM_GetAutoreload(TIM_TypeDef *timAddr) {
  assert_param(IS_TIM_ALL_PERIPH(timAddr));
  return (timAddr->ARR + 1);
}

PWM *new_PWM(void) {
  PWM *this = (PWM *)malloc(sizeof(PWM));
  PWM_Init(this);
  return this;
}

void delete_PWM(PWM *pwm) { free(pwm); }

void PWM_Init(PWM *this) {
  memset(this, 0, sizeof(PWM));
  this->freq = 1000;
  this->duty_pct = 0;
  this->type = PWM_Unipolar;
  this->timer = NULL;
  this->port[0] = NULL;
  this->port[1] = NULL;
}

/**
 * @brief   Configures the PWM.
 *
 * @param   freq  The PWM frequency in Hz.
 * @param   type  The PWM type, only unipolar is supported currently.
 * @param   timer The pointer to the timer peripheral to use.
 * @param   posPort The pointer to the positive GPIO port to use.
 * @param   negPort The pointer to the negative GPIO port to use.
 * @param   posPin The positive pin number.
 * @param   negPin The negative pin number.
 *
 * @note    The timer must have been initialized and the clock must have been
 * enabled for the timer to use.The frequency should between 1Hz and 1/10 of the
 * TIM interrupt frequency, otherwise the result is undefined. Better use a
 * factor of TIM interrupt frequency.
 */
void PWM_Config(PWM *this, uint32_t freq, PWM_Type type, TIM_TypeDef *timer,
                GPIO_TypeDef *posPort, GPIO_TypeDef *negPort, uint16_t posPin,
                uint16_t negPin) {
  this->freq = freq;
  this->type = type;

  this->timer = timer;
  this->port[0] = posPort;
  this->port[1] = negPort;
  this->pin[0] = posPin;
  this->pin[1] = negPin;

  // 端口初始化什么的先不检查了，应该不会吧……？
  // 算了，再初始化一遍就行
  // 认为时钟已经打开
  // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
  // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
  // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);
  // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);

  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  // GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin = this->pin[0];
  GPIO_Init(this->port[0], &GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = this->pin[1];
  GPIO_Init(this->port[1], &GPIO_InitStructure);

  // 计时器相关信息
  // 计时器初始化与否尚未检查
  assert_param(IS_TIM_ALL_PERIPH(timer));
  assert_param(timer->PSC == (TIM_DEFAULT_SCALE - 1));
  assert_param(freq > 0 && freq <= TIM_DEFAULT_CLK / 10);
  if (freq <= TIM_DEFAULT_CLK / 10) {
    uint32_t _TIMIntFreq =
        TIM_DEFAULT_CLK / _TIM_GetAutoreload(timer); // 计时器置数中断触发频率
    this->_TIMReloadCount = (_TIMIntFreq / freq);
  } else {
    // 该提升定时中断触发频率了！真执行到这里会出问题的
    // 除非你能想个办法直接通过计时器内部计数值来触发PWM步进
    // 至于如何利用计时器内部计数值，下面有一段示例，us倒计时

    // TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    // TIM_TimeBaseStructure.TIM_Period = 1;
    // TIM_TimeBaseStructure.TIM_Prescaler = (72 - 1); // 72M / 72 = 1us
    // TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down;
    // TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    // TIM_Cmd(TIM3, ENABLE);
    // TIM_SetCounter(TIM3, TIMCounter);
    // while (TIMCounter > 1) {
    //   TIMCounter = TIM_GetCounter(TIM3);
    // }
    // TIM_Cmd(TIM3, DISABLE);
    assert_param(0);
    this->_TIMReloadCount = 10;
  }
}

void PWM_SetDuty(PWM *this, int16_t duty_pct) {
  this->duty_pct = duty_pct;
  this->_pulseWidth = abs(duty_pct) * this->_TIMReloadCount / 100;
}

void PWM_Step(PWM *this) {
  this->_currentCount++;
  if (this->_currentCount >= this->_TIMReloadCount) {
    this->_currentCount = 0;
  }
  int sig = this->duty_pct > 0 ? 0 : 1;
  // 一个周期判断两次，给定值改动即可生效，因而对其响应最多延迟一个周期
  if (this->type == PWM_Unipolar) {
    if (this->_pulseWidth == 0) {
      // no pulse output
      GPIO_ResetBits(this->port[0], this->pin[0]);
      GPIO_ResetBits(this->port[1], this->pin[1]);
    } else if (this->_currentCount == 0) {
      // high/low pulse output
      GPIO_SetBits(this->port[sig], this->pin[sig]);
      GPIO_ResetBits(this->port[!sig], this->pin[!sig]);
    } else if (this->_currentCount == this->_pulseWidth) {
      // no pulse output
      GPIO_ResetBits(this->port[0], this->pin[0]);
      GPIO_ResetBits(this->port[1], this->pin[1]);
    }
  } else {
    // 为什么要写双极性呢？
    assert_param(0);
  }
}
